package com.wolfking.file.web;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.wolfking.jeesite.sign.Sign;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import java.awt.*;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

@Slf4j
@Controller
@RequestMapping("/file")
public class FileController {
    @Sign
    @ResponseBody
    @RequestMapping(value = "/treeData", method = RequestMethod.GET)
    public List<Map<String, Object>> treeData(@RequestParam(value = "id", defaultValue = "") String id,
                                              @RequestParam(value = "fileSource", defaultValue = "") String fileSource) {
        log.info("treeData fileSource is {},id is {}", fileSource, id);
        List<Map<String, Object>> mapList = Lists.newArrayList();
        File rootFile = new File(StringUtils.isEmpty(id) ? "H:\\" : id);
        if (StringUtils.isEmpty(id)) {
            Map<String, Object> map = Maps.newHashMap();
            map.put("id", rootFile.getAbsolutePath());
            map.put("pId", rootFile.getAbsolutePath());
            map.put("name", StringUtils.isNotBlank(rootFile.getName()) ? rootFile.getName() : rootFile.getName() + "H:\\");
            map.put("isParent", rootFile.isDirectory());
            map.put("fileSource", fileSource);
            mapList.add(map);
        } else {
            File[] files = rootFile.listFiles();
            if (files != null) {
                for (File file : files) {
                    Map<String, Object> map = Maps.newHashMap();
                    map.put("id", file.getAbsolutePath());
                    map.put("pId", rootFile.getAbsolutePath());
                    map.put("name", file.getName());
                    map.put("fileSource", fileSource);
                    map.put("isParent", file.isDirectory());
                    mapList.add(map);
                }
            }
        }
        return mapList;
    }

    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index(Model model, @RequestParam(value = "fileSource", defaultValue = "") String fileSource) {
        log.info("fileSource is {}", fileSource);
        model.addAttribute("fileSource", fileSource);
        model.addAttribute("rootPId", "H:\\");
        return "file/index";
    }

    @RequestMapping(value = "/fileDetail", method = RequestMethod.GET)
    public ResponseEntity<?> fileDetail(
            @RequestParam(value = "fileSource", defaultValue = "") String fileSource,
            @RequestParam(value = "id") String id) throws Exception {
        log.info("fileSource is {}", fileSource);
        HttpHeaders headers = new HttpHeaders();
        final byte[] bytes = Files.toByteArray(new File(id));
        if (isImage(new ByteArrayInputStream(bytes))) {
            //设置ContentType的值 IMAGE_JPEG在浏览器返回图片
            headers.setContentType(new MediaType("image", id.substring(id.lastIndexOf(".") + 1)));
            // 内容是字节流
            return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        }
        if (id.endsWith(".pdf")) {
            //设置ContentType的值 IMAGE_JPEG在浏览器返回图片
            headers.setContentType(MediaType.APPLICATION_PDF);
            // 内容是字节流
            return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        }
        if (isText(new ByteArrayInputStream(bytes))) {
            //设置ContentType的值 IMAGE_JPEG在浏览器返回图片
            headers.setContentType(new MediaType("text/plain; charset=" + getFileCharset(new ByteArrayInputStream(bytes))));
            // 内容是字节流
            return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        }
        return new ResponseEntity<>(new byte[0], headers, HttpStatus.OK);
    }


    @RequestMapping(value = "/downloadFile", method = RequestMethod.GET)
    public ResponseEntity<?> downloadFile(
            @RequestParam(value = "fileSource", defaultValue = "") String fileSource,
            @RequestParam(value = "id") String id) throws Exception {
        log.info("fileSource is {},id is {}", fileSource, id);
        final File file = new File(id);
        HttpHeaders headers = new HttpHeaders();
        if (!file.isDirectory()) {
            headers.set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            headers.set("Content-Disposition",
                    String.format("attachment; filename=%s", URLEncoder.encode(file.getName(), "UTF-8")));
            try {
                return new ResponseEntity<>(Files.toByteArray(file), headers, HttpStatus.OK);
            } catch (Exception e) {
                log.error("", e);
            }
        }
        return new ResponseEntity<>(new byte[0], headers, HttpStatus.OK);
    }

    @RequestMapping(value = "/openFile", method = RequestMethod.GET)
    public ResponseEntity<?> openFile(
            @RequestParam(value = "fileSource", defaultValue = "") String fileSource,
            @RequestParam(value = "id") String id) throws Exception {
        return fileDetail(fileSource, id);
    }


    public static boolean isImage(InputStream inputStream) {
        try {
            Image img = ImageIO.read(inputStream);
            if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) {
                return false;
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private static boolean isText(InputStream inputStream) {
        boolean isText = true;
        try {
            long len = inputStream.available();
            for (int j = 0; j < (int) len; j++) {
                int t = inputStream.read();
                if (t < 32 && t != 9 && t != 10 && t != 13) {
                    isText = false;
                    break;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return isText;
    }


    /**
     * 获取文件的编码格式
     */

    private static String getFileCharset(InputStream inputStream) {
        String charset = "GBK";
        byte[] first3Bytes = new byte[3];
        try {
            boolean checked = false;
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            bis.mark(0);
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1) {
                return charset; // 文件编码为 ANSI
            } else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
                charset = "UTF-16LE"; // 文件编码为 Unicode
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
                charset = "UTF-16BE"; // 文件编码为 Unicode big endian
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF) {
                charset = "UTF-8"; // 文件编码为 UTF-8
                checked = true;
            }
            bis.reset();
            if (!checked) {
                int loc = 0;
                while ((read = bis.read()) != -1) {
                    loc++;
                    if (read >= 0xF0)
                        break;
                    if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的，也算是GBK
                        break;
                    if (0xC0 <= read && read <= 0xDF) {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
// (0x80
// - 0xBF),也可能在GB编码内
                            continue;
                        else
                            break;
                    } else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错，但是几率较小
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) {
                                charset = "UTF-8";
                                break;
                            } else
                                break;
                        } else
                            break;
                    }
                }
            }
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return charset;
    }
}
